//------------------------------------------------------------------------------
// RegAccessor.cpp
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// URL Accessor implementation for the registry indexer
//
//------------------------------------------------------------------------------

#include "pch.h"
#include "resids.h"
#include <searchapi.h>
#include "RegPH.h"
#include "RegAccessor.h"
#include "PropVariant.h"
#include "RegFilter.h"
#include <shobjidl.h>

HRESULT CRegAccessor::s_CreateInstance(PCWSTR pszPath, REFIID riid, __deref_out void **ppv)
{    
    *ppv = NULL;
    CComObjectWithRef<CRegAccessor> *pRegAccessor;
    HRESULT hr = CComObjectWithRef<CRegAccessor>::CreateInstance(&pRegAccessor);
    if (SUCCEEDED(hr))
    {
        hr = pRegAccessor->_Initialize(pszPath);
        if (SUCCEEDED(hr))
        {
            hr = pRegAccessor->QueryInterface(riid, ppv);
        }
        pRegAccessor->Release();
    }
    return hr;
}

HRESULT CRegAccessor::_Initialize(PCWSTR pszPath)
{    
    HRESULT hr = SHCreateItemFromParsingName(pszPath, NULL, IID_PPV_ARGS(&_spItem));
	if (SUCCEEDED(hr))
	{
        CComPropVariant spropvarFileAttributes;
        if (SUCCEEDED(_spItem->GetProperty(PKEY_FileAttributes, &spropvarFileAttributes)))
        {
			DWORD dwFileAttrib;
			if (SUCCEEDED(PropVariantToUInt32(spropvarFileAttributes, &dwFileAttrib)) 
				&& (dwFileAttrib & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
			{
				// not too sure what the best error is here, but it's nice to have 
				// it be distinguishable from E_FAIL
				hr = E_ABORT;
			}
        }
	}
	return hr;
}

// IUrlAccessor
STDMETHODIMP CRegAccessor::AddRequestParameter(PROPSPEC *pSpec, PROPVARIANT *pVar)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetDocFormat(__out_ecount_part(dwSize, *pdwLength) WCHAR wszDocFormat[], DWORD dwSize, __out DWORD *pdwLength)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetCLSID(__out CLSID *pClsid)
{
    *pClsid = __uuidof(RegAccessor);
    return S_OK;
}

STDMETHODIMP CRegAccessor::GetHost(__out_ecount_part(dwSize, *pdwLength) WCHAR wszHost[], DWORD dwSize, __out DWORD *pdwLength)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::IsDirectory(void)
{
    DWORD ffAttributes = 0;
    HRESULT hr = _spItem->GetAttributes(SFGAO_FOLDER, &ffAttributes);
    if (SUCCEEDED(hr))
    {
        hr = (ffAttributes & SFGAO_FOLDER) ? S_OK : S_FALSE;
    }
    return hr;
}

STDMETHODIMP CRegAccessor::GetSize(__out ULONGLONG *pllSize)
{
    // the size for a root, key, or value??
    // we could return the size of the data in the registry
    *pllSize = 0;
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetLastModified(__out FILETIME *pftLastModified)
{
    CComPtr<IShellItem2> spItem2;
    HRESULT hr = _spItem->QueryInterface(&spItem2);
    if (SUCCEEDED(hr))
    {
        if (IsDirectory() == S_OK)
        {
            SYSTEMTIME systime;
            GetSystemTime(&systime);
            hr = SystemTimeToFileTime(&systime, pftLastModified) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
        }
        else if (SUCCEEDED(hr))
        {
            CComPropVariant spropvarModified;
            hr = spItem2->GetProperty(PKEY_DateModified, &spropvarModified);
            if (SUCCEEDED(hr))
            {
                hr = PropVariantToFileTime(spropvarModified, PSTF_UTC, pftLastModified);
            }
        }
    }

    return hr;
}

STDMETHODIMP CRegAccessor::GetFileName(__out_ecount_part(dwSize, *pdwLength) WCHAR wszFileName[], DWORD dwSize, __out DWORD *pdwLength)
{
    // if we return a value here, bindtofilter is not called
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetSecurityDescriptor(__out_ecount_full(dwSize) BYTE *pSD, DWORD dwSize, __out DWORD *pdwLength)
{
    // do we need this?  we're in user-mode for the protocol handler
    // and we should only be enumerating what we (the user) can see.
    // do we still need this?  I *think* this is only for the
    // system context enumerators and we implicitly get the user's
    // security descriptor.

    // from http://windowssdk.msdn.microsoft.com/en-gb/library/ms631508.aspx
    // If this method is not implemented, the item will be retrievable by all user queries. 
    // This method also allows custom mappings between users registered to a content source 
    // and those users registered on the domain, if they are different. Security descriptors 
    // created in this method must be self-relative. 

    // per eric - we don't need it.  Maybe we should file a doc bug to fix the above reference

    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetRedirectedURL(__out_ecount_part(dwSize, *pdwLength) WCHAR wszRedirectedURL[], DWORD dwSize, __out DWORD *pdwLength)
{
    // there is no URL redirection per se - we could use this to record mirroring and/or virtualization
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::GetSecurityProvider(__out CLSID *pSPClsid)
{
    // default to NT security
    return S_FALSE;
}

STDMETHODIMP CRegAccessor::BindToStream(__deref_out IStream **ppStream)
{
    // from http://windowssdk.msdn.microsoft.com/en-us/library/ms631493.aspx
    // we can implement BindToFilter, this, or both.  Let's just do BindToFilter
    
    return E_NOTIMPL;
}

STDMETHODIMP CRegAccessor::BindToFilter(__deref_out IFilter **ppFilter)
{
    // consider: PRTH_E_OBJ_NOT_FOUND, PRTH_S_NOT_MODIFIED       
    return CRegFilter::s_CreateInstance(_spItem, IID_PPV_ARGS(ppFilter));
}

// IUrlAccessor2
STDMETHODIMP CRegAccessor::GetDisplayUrl(__out_ecount_part(dwSize, *pdwLength) WCHAR wszDocUrl[], DWORD dwSize, __out DWORD *pdwLength)
{
    PWSTR pszName;
    HRESULT hr = _spItem->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName);
    if (SUCCEEDED(hr))
    {
        size_t cchLength;
        hr = StringCchCopyEx(wszDocUrl, dwSize, pszName, NULL, &cchLength, STRSAFE_NULL_ON_FAILURE);

        *pdwLength = (DWORD)cchLength;
        CoTaskMemFree(pszName);
    }

    return hr;
}

STDMETHODIMP CRegAccessor::IsDocument(void)
{
    return (IsDirectory() == S_OK) ? S_FALSE : S_OK;
}

STDMETHODIMP CRegAccessor::GetCodePage(__out_ecount_part(dwSize, *pdwLength) WCHAR wszCodePage[], DWORD dwSize, __out DWORD *pdwLength)
{
    return E_NOTIMPL;
}
